// Copyright 2022 NullPTR. All Rights Reserved.


#include "FSDFUtils.h"

namespace SDFGen
{
	FSDFUtils::FSDFUtils() : width(0), height(0), grid1(), grid2()
	{
	}

	FSDFUtils::~FSDFUtils()
	{
		if (grid1.grid)
		{
			delete[] grid1.grid;
			grid1.grid = nullptr;
		}

		if (grid2.grid)
		{
			delete[] grid2.grid;
			grid2.grid = nullptr;
		}
	}

	void FSDFUtils::Generate(const FRGBABitmap* src, FRGBABitmap* dest)
	{
		if (src == nullptr || dest == nullptr)
		{
			return;
		}

		width = src->GetWidth();
		height = src->GetHeight();

		grid1.grid = new Point[width * height];
		grid2.grid = new Point[width * height];

		for(int y = 0; y < height; y++)
		{
			for (int x = 0; x < width; x++)
			{
				unsigned char r, g, b;
				src->GetRGB(x, y, r, g, b);

				// Points inside get marked with a dx/dy of zero.
				// Points outside get marked with an infinitely large distance.
				// ��������һ���ڲ����ó�0���ⲿ���ó��������һ�������෴��
				if (g < 128)
				{
					PutGrid(grid1, x, y, INSIDE_POINT);
					PutGrid(grid2, x, y, EMPTY_POINT);
				} else {
					PutGrid(grid2, x, y, INSIDE_POINT);
					PutGrid(grid1, x, y, EMPTY_POINT);
				}
			}
		}

		GeneratePass(grid1);
		GeneratePass(grid2);

		dest->Create(width, height);

		for(int y = 0; y < height; y++)
		{
			for (int x = 0; x < width; x++)
			{
				// Calculate the actual distance from the dx/dy
				//����ƫ��ֵ�ĵ���뵱ǰ��ľ���
				int dist1 = (int)(sqrt((double)GetGrid(grid1, x, y).DistSq()));
				int dist2 = (int)(sqrt((double)GetGrid(grid2, x, y).DistSq()));
				int dist = dist1 - dist2;

				// Clamp and scale it, just for display purposes.
				int c = dist * 3 + 128;
				if (c < 0) c = 0;
				if (c > 255) c = 255;

				dest->SetRGB(x, y, c, c, c);
			}
		}
	}

	void FSDFUtils::Generate(const FImage* src, FImage* dest)
	{
		if (src == nullptr || dest == nullptr)
		{
			return;
		}

		width = src->GetWidth();
		height = src->GetHeight();

		grid1.grid = new Point[width * height];
		grid2.grid = new Point[width * height];

		for(int y = 0; y < height; y++)
		{
			for (int x = 0; x < width; x++)
			{
				//unsigned char r, g, b;
				//src->GetRGB(x, y, r, g, b);

				void* Pointer=src->GetPixelPointer(x,y);
				uint8* PixelPointer=static_cast<uint8*>(Pointer);
				
				// Points inside get marked with a dx/dy of zero.
				// Points outside get marked with an infinitely large distance.
				// ��������һ���ڲ����ó�0���ⲿ���ó��������һ�������෴��
				if (*PixelPointer < 128)
				{
					PutGrid(grid1, x, y, INSIDE_POINT);
					PutGrid(grid2, x, y, EMPTY_POINT);
				} else {
					PutGrid(grid2, x, y, INSIDE_POINT);
					PutGrid(grid1, x, y, EMPTY_POINT);
				}
			}
		}

		GeneratePass(grid1);
		GeneratePass(grid2);

		//dest->Create(width, height);
		dest->Init(width,height,ERawImageFormat::G8);

		for(int y = 0; y < height; y++)
		{
			for (int x = 0; x < width; x++)
			{
				// Calculate the actual distance from the dx/dy
				//����ƫ��ֵ�ĵ���뵱ǰ��ľ���
				int dist1 = (int)(sqrt((double)GetGrid(grid1, x, y).DistSq()));
				int dist2 = (int)(sqrt((double)GetGrid(grid2, x, y).DistSq()));
				int dist = dist1 - dist2;

				// Clamp and scale it, just for display purposes.
				int c = dist * 3 + 128;
				if (c < 0) c = 0;
				if (c > 255) c = 255;

				//dest->SetRGB(x, y, c, c, c);
				void* Pointer= dest->GetPixelPointer(x,y);
				uint8* Uint8Pointer=static_cast<uint8*>(Pointer);
				*Uint8Pointer=c;
			}
		}
	}

	FSDFUtils::Point FSDFUtils::GetGrid(const Grid& g, int x, int y) const
	{
		// OPTIMIZATION: you can skip the edge check code if you make your grid
		// have a 1-pixel gutter.
		if ( x >= 0 && y >= 0 && x < width && y < height )
			return *(g.grid + y * width + x);
		else
			return EMPTY_POINT;
	}

	void FSDFUtils::PutGrid(Grid& g, int x, int y, const Point& p)
	{
		*(g.grid + y * width + x) = p;
	}

	void FSDFUtils::Compare(const Grid& g, Point& p, int x, int y, int offsetx, int offsety)
	{
		Point other = GetGrid(g, x + offsetx, y + offsety);

		other.dx += offsetx;
		other.dy += offsety;

		if (other.DistSq() < p.DistSq())
		{
			p = other;
		}
	}

	void FSDFUtils::GeneratePass(Grid& g)
	{
		// Pass 0
		for (int y=0; y < height; y++)
		{
			for (int x=0; x<width; x++)
			{
				Point p = GetGrid( g, x, y );
				Compare( g, p, x, y, -1,  0 );
				Compare( g, p, x, y,  0, -1 );
				Compare( g, p, x, y, -1, -1 );
				Compare( g, p, x, y,  1, -1 );
				PutGrid( g, x, y, p );
			}

			for (int x=width-1;x>=0;x--)
			{
				Point p = GetGrid( g, x, y );
				Compare( g, p, x, y, 1, 0 );
				PutGrid( g, x, y, p );
			}
		}

		// Pass 1
		for (int y=height-1;y>=0;y--)
		{
			for (int x=width-1;x>=0;x--)
			{
				Point p = GetGrid( g, x, y );
				Compare( g, p, x, y,  1,  0 );
				Compare( g, p, x, y,  0,  1 );
				Compare( g, p, x, y, -1,  1 );
				Compare( g, p, x, y,  1,  1 );
				PutGrid( g, x, y, p );
			}

			for (int x=0;x<width;x++)
			{
				Point p = GetGrid( g, x, y );
				Compare( g, p, x, y, -1, 0 );
				PutGrid( g, x, y, p );
			}
		}
	}
}
